package com.onpositive.knowledge.model.values;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.onpositive.knowledge.model.value.audits.AbstractAuditResult;
import com.onpositive.knowledge.model.value.audits.GeneralAuditResult;
import com.onpositive.knowledge.model.value.audits.WikiLinksDisambiguationAuditResult;
import com.onpositive.knowledge.model.value.audits.WikiLinksStatisticAudit;
import com.onpositive.knowledge.model.values.ReplacementProposals.Proposal;
import com.onpositive.semantic.model.api.property.IProperty;
import com.onpositive.units.ParsedIdent;
import com.onpositive.units.ParsedString;
import com.onpositive.units.ParsedValue;
import com.onpositive.units.ParsedValueSequence;

public class WikiLinksRefiningTransformer extends AuditAssistedTransformer {

	private static final String GENERAL_RESULT_META_KEY = "general-result";
	private static final String PROPERTY_FOCUSED_RESULT_META__KEY = "property-result";
	/**
	 * 
	 */
	private static final long serialVersionUID = 5820064548264358145L;
	public final static String COMMENT_ = "Links refining audit" ;
	public static final String ID = "wiki-links-refining-transformer";
	
	public WikiLinksRefiningTransformer(IHasAuditResult source) {
		super(source);
		// TODO Auto-generated constructor stub
	}
	
	@Override
	public DescribedValue[] adjust(Object object, IProperty prop, DescribedValue... original) {

		AbstractAuditResult aResult = this.getAuditResultSource().getAuditResult() ;
	
		WikiLinksDisambiguationAuditResult gRes = null ;		
		
		if( aResult instanceof WikiLinksDisambiguationAuditResult ){
			gRes = (WikiLinksDisambiguationAuditResult) aResult ;
		}		
		else if( aResult instanceof GeneralAuditResult )
		{			
			GeneralAuditResult generalResult = (GeneralAuditResult) aResult ;
			AbstractAuditResult _res = generalResult.getResult( WikiLinksStatisticAudit.ID ) ;
			if( _res instanceof WikiLinksDisambiguationAuditResult )
				gRes = (WikiLinksDisambiguationAuditResult) _res ;
		}
		if( gRes == null )
			return original ;
		
		WikiLinksDisambiguationAuditResult pRes = gRes.getPropertyResult( prop.getId() ) ;
		
		HashMap<String,Object> meta = new HashMap<String, Object>() ;
		meta.put(PROPERTY_FOCUSED_RESULT_META__KEY, pRes) ;
		meta.put(GENERAL_RESULT_META_KEY , gRes) ;
		
		Object processed = traverser.iterateValues(null, original, false, meta, 0,false);
		if( !(processed instanceof Object[]) )
			return original ;
		
		Object[] arr = (Object[]) processed ;
		ArrayList<DescribedValue> lst = new ArrayList<DescribedValue>() ; 
		for( Object o : arr ){
			if( o instanceof DescribedValue )
				lst.add((DescribedValue) o) ;
			else
				lst.add( null ) ;
		}
		
		return lst.toArray( new DescribedValue[lst.size()] ) ;
	}
	
	

	ModifyingValueTraverser traverser = new ModifyingValueTraverser() {
		
		@SuppressWarnings("unused")
		@Override
		protected Object process(IProperty prop, Object val, Map<String,Object> meta, int index,boolean cameFromCollection)
		{
			if( !(val instanceof ParsedIdent) )
				return null ;
			
			ParsedIdent ident = (ParsedIdent) val ;
			AbstractAuditResult _result = (AbstractAuditResult) meta.get(PROPERTY_FOCUSED_RESULT_META__KEY) ;
			
			if( _result instanceof WikiLinksDisambiguationAuditResult ){			
				WikiLinksDisambiguationAuditResult result = (WikiLinksDisambiguationAuditResult) _result ;
				Object processed = doProcess(ident, result);
				if( processed instanceof ParsedIdent ){
					ParsedIdent processedident = (ParsedIdent) processed; 
					return processed;
				}
				else if( processed != null )
					return null ;
			}
			
			_result = (AbstractAuditResult) meta.get(GENERAL_RESULT_META_KEY) ;
			
			if( _result instanceof WikiLinksDisambiguationAuditResult ){			
				WikiLinksDisambiguationAuditResult result = (WikiLinksDisambiguationAuditResult) _result ;
				Object processed = doProcess(ident, result);
				if( processed instanceof ParsedIdent ){
					ParsedIdent processedident = (ParsedIdent) processed; 
					return processed;
				}
				else if( processed != null )
					return null ;
			}
			
			return null ;			
		}

				
		@Override
		protected Class<?>[] createTargetClasses() {
			return new Class<?>[]{ ParsedIdent.class };
		}
		
		@Override
		protected Class<?>[] createNecessaryClasses() {
			return new Class<?>[]{ ParsedIdent.class };
		}
	};
	
	private static Object doProcess(ParsedIdent ident, WikiLinksDisambiguationAuditResult result)
	{
		HashMap<String,String> clMap = result.getClMap() ;
		HashMap<String,String> lcMap = result.getLcMap() ;
		HashMap<String,String> linkCaseMap = result.getLinksCaseMap() ;
		HashMap<String,String> captionCaseMap = result.getCaptionCaseMap() ;
		ReplacementProposals linkProposlas = result.getLinkProposals() ;
		ReplacementProposals captionProposlas = result.getCaptionProposals() ;		
		
		boolean lChange = false ;
		boolean cChange = false ;
		
		String linkString = ident.getRef() ;
		String captionString = ident.getCaption() ;
		
		if( linkString == null && captionString == null )
			return null ;
		
		String linkString_lc = null, captionString_lc = null ;
		
		if( linkString != null ){
			linkString_lc = linkString.toLowerCase() ;
			if( linkProposlas != null )
			{				
				Proposal linkProposal = linkProposlas.getBestProposal( linkString_lc ) ;				
				linkString_lc = linkProposal != null ? (String) linkProposal.getVlaue() : linkString_lc ;
				lChange = lChange || linkProposal != null ;
			}
		}
		
		if( captionString != null )
		{			
			captionString_lc = captionString.toLowerCase() ;
			if( captionProposlas != null ){										
				Proposal captionProposal = captionProposlas.getBestProposal( captionString_lc ) ;				
				captionString_lc = captionProposal != null ? (String) captionProposal.getVlaue() : captionString_lc ;
				cChange = cChange || captionProposal != null ;
			}
		}
		
		if( linkString_lc == null ){
			linkString_lc = clMap.get( captionString_lc ) ;
			lChange = lChange || linkString_lc != null ;
		}
		
		if( linkString_lc == null )
			return null ;
			
		String newCaptionString_lc = lcMap.get( linkString_lc ) ;
		cChange = cChange || ( newCaptionString_lc != null && newCaptionString_lc != captionString_lc ) ;
		captionString_lc = newCaptionString_lc != null ? newCaptionString_lc : captionString_lc ;
		
		if( captionString_lc == null )
			captionString_lc = linkString_lc ;
		
		String newLinkString_lc = clMap.get( captionString_lc ) ;
		lChange = lChange || ( newLinkString_lc != null && newLinkString_lc != linkString_lc );
		linkString_lc = newLinkString_lc != null ? newLinkString_lc : linkString_lc ;			
			
		if( !(lChange || cChange) )
			return null ;
		
		String newLinkString = linkCaseMap.get( linkString_lc ) ;
		newLinkString = newLinkString == null ? linkString : newLinkString ;
		
		String newCaptionString = captionCaseMap.get( captionString_lc ) ;
		newCaptionString = newCaptionString == null ? captionString : newCaptionString ;
		
		if( !Character.isUpperCase( newCaptionString.charAt(0) ) )
		{
			StringBuilder bld = new StringBuilder(newCaptionString) ;
			bld.setCharAt(0, Character.toUpperCase( bld.charAt(0) )) ;
			newCaptionString = bld.toString() ;
		}
		
		if( newLinkString.equals(linkString) && newCaptionString.equals(captionString) )
			return 0 ;	
		
		return new ParsedIdent( newLinkString , newCaptionString, ID ) ;
	}

	
	private DescribedValue[] doAdjust(DescribedValue[] original, WikiLinksDisambiguationAuditResult pRes, WikiLinksDisambiguationAuditResult gRes)
	{
		ArrayList<DescribedValue> result = new ArrayList<DescribedValue>() ;
		ArrayList<ParsedValue> pvArr = new ArrayList<ParsedValue>() ;
		
		boolean gotAGlobalChange = false ;
		for( DescribedValue dv : original )
		{
			Object valueObject = dv.value();
			if( valueObject == null || !(valueObject instanceof ParsedValueSequence))
			{
				result.add(dv) ;
				continue ;
			}
			
			ParsedValueSequence sequence = (ParsedValueSequence) valueObject ;
			Object values = sequence.value() ;
			if( !(values instanceof Collection<?>) ){
				result.add(dv) ;
				continue ;
			}
			
			Collection<?> col = (Collection<?>) values ;
			pvArr.clear() ;
			boolean gotALocalChange = false ;
			for( Object o : col )
			{
				if( !(o instanceof ParsedString) )
				{
					pvArr.add( (ParsedValue) o ) ;
					continue ;
				}
				final ParsedString oldPs = (ParsedString)o;
				String valString = (String) oldPs.value() ;
				
				//String valString = "[[|fighter]] [[|inline_engine]]" ;
				
				ArrayList<String> words = split( valString ) ;
				
				
				for( int i = 0 ; i < words.size() ; i++ )
				{
					String w = words.get(i);
					String v = null ;					
					if( pRes != null )
						v = process( w, pRes ) ;
					if(  v == null && gRes != null )
						v = process( w, gRes ) ;
					
					v = v != null ? v : w ;
					v = inheritMissingCaptionsFromLinks(v) ;
					
					if( v != null && !v.trim().equals( w ) )
					{						
						gotALocalChange = true ;
						words.set(i, v) ;
					}
				}
				
				if( gotALocalChange ){
					String newString = concatenate( words ) ;
					pvArr.add( new ParsedString( newString, "lnk-ref " + oldPs.getComment() ) ) ;
				}
				else
					pvArr.add( (ParsedValue) o ) ;					
			}
			if( gotALocalChange )
			{
				gotAGlobalChange = true ;
				result.add( new DescribedValue( new ParsedValueSequence( pvArr, "lnk-ref " + sequence.getComment()), COMMENT_ ) ) ;				
			}
			else
				result.add(dv) ;
			
		}	
		return gotAGlobalChange ? result.toArray( new DescribedValue[result.size()] ) : original ;
	}

	private String concatenate(ArrayList<String> words)
	{
		StringBuilder bld = new StringBuilder() ;
		for( String s : words )
		{
			bld.append( s ) ;
			bld.append( " " ) ;
		}
		return bld.substring(0, bld.length()-1);
	}

	private ArrayList<String> split(String str)
	{
		ArrayList<String> result = new ArrayList<String>() ;
		int prev = 0 ;
		for( int i = str.indexOf("[[") ; i >= 0 ; i = str.indexOf("[[", prev ) )
		{
			String s = str.substring(prev, i).trim() ;
			if( s.length() != 0 )
				result.add( s ) ;
			
			prev = str.indexOf("]]",i) + 2 ;
			result.add(str.substring(i, prev)) ;
		}
		String s = str.substring(prev, str.length() ).trim() ;
		if( s.length() != 0 )
			result.add( s ) ;
		
		return result;
	}

	@SuppressWarnings("unused")
	private String process(String str, WikiLinksDisambiguationAuditResult result )
	{
		boolean tmp = str.toLowerCase().startsWith("vickers") ;
//		str = " [[Vickers Limited]]" ;
		//str = "[[Imperial Japanese Army Air Service|IJA Air Service]]" ;
		if( str == null )
			return null ;
		
		int i = str.indexOf("[[") ;
		if( i < 0 )
			return null;

		StringBuilder builder = new StringBuilder() ;
		HashMap<String,String> clMap = result.getClMap() ;
		HashMap<String,String> lcMap = result.getLcMap() ;
		HashMap<String,String> linkCaseMap = result.getLinksCaseMap() ;
		HashMap<String,String> captionCaseMap = result.getCaptionCaseMap() ;
		ReplacementProposals linkProposlas = result.getLinkProposals() ;
		ReplacementProposals captionProposlas = result.getCaptionProposals() ;
		
		boolean lChange = false ;
		boolean cChange = false ;
		int prev = 0 ;
		for(  ; i >=0 ; i = str.indexOf("[[",i )  )
		{			
			i+=2 ;
			builder.append( str.substring(prev,i) ) ;
			
			prev = str.indexOf( "]]", i ) ;
			String fullLinkString = str.substring( i,prev ).trim() ;			
			int spl = fullLinkString.indexOf("|") ;
			
			i = prev+2 ;
			String linkString, captionString ;
			if( spl < 0 )//[[*****]]
			{
				linkString = fullLinkString ;
				captionString = linkString ; 
			}
			else{
				linkString = fullLinkString.substring( 0, spl ).trim() ;
				captionString = fullLinkString.substring(spl+1).trim() ;				
			}
			String linkString_lc = linkString.toLowerCase() ;
			String captionString_lc = captionString.toLowerCase() ;
			
			Proposal linkProposal = linkProposlas.getBestProposal( linkString_lc ) ;				
			linkString_lc = linkProposal != null ? (String) linkProposal.getVlaue() : linkString_lc ;
			lChange = lChange || linkProposal != null ; 
							
			Proposal captionProposal = captionProposlas.getBestProposal( captionString_lc ) ;				
			captionString_lc = captionProposal != null ? (String) captionProposal.getVlaue() : captionString_lc ;
			cChange = cChange || captionProposal != null ;
			
			if( linkString_lc.length() == 0 ){
				linkString_lc = clMap.get( captionString_lc ) ;
				lChange = lChange || linkString_lc != null ;
				linkString_lc = linkString_lc != null ? linkString_lc : "" ;				 
			}			
				
			String newCaptionString_lc = lcMap.get( linkString_lc ) ;
			captionString_lc = newCaptionString_lc != null ? newCaptionString_lc : captionString_lc ;
			cChange = cChange || newCaptionString_lc != null ;
			
			String newLinkString_lc = clMap.get( captionString_lc ) ;
			linkString_lc = newLinkString_lc != null ? newLinkString_lc : linkString_lc ;
			lChange = lChange || newLinkString_lc != null ;
			
			String newLinkString = linkCaseMap.get( linkString_lc ) ;
			newLinkString = newLinkString == null ? linkString : newLinkString ;
			
			String newCaptionString = captionCaseMap.get( captionString_lc ) ;
			newCaptionString = newCaptionString == null ? captionString : newCaptionString ;
				
			builder.append( newLinkString ) ;
			if( captionString != null )
			{
				builder.append( "|" ) ;
				builder.append( newCaptionString ) ;					
			}	
		}
		builder.append( str.substring(prev, str.length() ) ) ;

		String resultingString = lChange || cChange ? builder.toString().trim() : null;
		return resultingString ;
	}
	
	private String inheritMissingCaptionsFromLinks(String str) {
		
		if( str == null )
			return null ;
		
		int i = str.indexOf("[[") ;
		if( i < 0 )
			return null;

		StringBuilder builder = new StringBuilder() ;
		boolean gotAChange = false ;
		int prev = 0 ;
		for(  ; i >=0 ; i = str.indexOf("[[",i )  )
		{
			i+=2 ;
			builder.append( str.substring(prev,i) ) ;
			
			prev = str.indexOf( "]]", i ) ;
			String fullLinkString = str.substring( i,prev ).trim() ;
			i = prev+2 ;
			
			int spl = fullLinkString.indexOf("|") ;				
			builder.append( fullLinkString ) ;
			if( spl < 0 )
			{
				gotAChange = true ;				
				builder.append( "|" ) ;
				builder.append( fullLinkString ) ;				
			}
		}
		builder.append( str.substring(prev, str.length() ) ) ;
		return gotAChange ? builder.toString().trim() : str ;
	}

	@Override
	public String getId() {
		return ID;
	}
	
}
